﻿using HybridCLR.Editor.Settings;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
using XFABManager;

namespace XFGameFramework.HybridCLR
{

    public enum HotUpdateType { 
        Assembly,
        Dll
    }

    [Serializable]
    public class HotUpdateInfo 
    {
        public HotUpdateType type;
        public AssemblyDefinitionAsset asset;
        public DefaultAsset dll;

        /// <summary>
        /// 加载顺序 , 值越小 越先加载
        /// </summary>
        public int loading_order;


        public string GetName()
        {

            string path = string.Empty;

            switch (type)
            {
                case HotUpdateType.Assembly:

                    path = AssetDatabase.GetAssetPath(asset);

                    if(string.IsNullOrEmpty(path))
                        return string.Empty;

                    JObject obj = JsonConvert.DeserializeObject<JObject>( File.ReadAllText(path));

                    return obj["name"].ToString(); 

                case HotUpdateType.Dll: 
                    path = AssetDatabase.GetAssetPath(dll); 
                    return Path.GetFileNameWithoutExtension(path);
            }


            return string.Empty;
        }

    }
     
    [Serializable]
    public class HybridCLRSettingInfo 
    {
        public string projectName
        {
            get 
            {
                
                string path = AssetDatabase.GUIDToAssetPath(guid);
                XFABProject project = AssetDatabase.LoadAssetAtPath<XFABProject>(path);
                if (project == null) return string.Empty;
                return project.name;
            }
        }

        public string guid;
        public List<HotUpdateInfo> infos = new List<HotUpdateInfo>();
        public bool foldout = true;

        public List<AssemblyDefinitionAsset> GetAssemblies() 
        {
            List<AssemblyDefinitionAsset> assemblies = new List<AssemblyDefinitionAsset>();

            foreach (var item in infos)
            {
                if (item.type != HotUpdateType.Assembly) 
                    continue;

                if(item.asset == null)
                    continue;

                if (assemblies.Contains(item.asset))
                    continue;

                assemblies.Add(item.asset);
            }

            return assemblies;
        }


        public List<DefaultAsset> GetDlls()
        {
            List<DefaultAsset> dlls = new List<DefaultAsset>();

            foreach (var item in infos)
            {
                if (item.type != HotUpdateType.Dll)
                    continue;

                if (item.dll == null) 
                    continue;

                // 判断是否是 dll 
                string path = AssetDatabase.GetAssetPath(item.dll);

                if(string.IsNullOrEmpty(path))
                    continue;

                string extension = Path.GetExtension(path).ToLower();

                if (extension != ".dll")
                {
                    continue;
                }
                 
                dlls.Add(item.dll);
            }

            return dlls;
        }

    }

    public class XFHybridCLRSettings : ScriptableObject
    {
        #region 静态字段

        public static XFHybridCLRSettings _instance;
         
        public static XFHybridCLRSettings Instance
        {
            get 
            {
                string path = string.Format("{0}/../ProjectSettings/XFGameFramework_HybridCLRSettings.asset",Application.dataPath);

                if(_instance != null) 
                    return _instance;

                UnityEngine.Object[] objects = InternalEditorUtility.LoadSerializedFileAndForget(path);
                if(objects != null &&  objects.Length > 0)
                {
                    foreach (var item in objects)
                    {
                        _instance = item as XFHybridCLRSettings;
                        if (_instance != null) break;
                    }
                }

                if (_instance == null) 
                {
                     _instance = CreateInstance<XFHybridCLRSettings>();
                    InternalEditorUtility.SaveToSerializedFileAndForget(new UnityEngine.Object[] { _instance }, path, true);
                }

                return _instance;
            }
        }
        
        
        #endregion


        public List<HybridCLRSettingInfo> settings = new List<HybridCLRSettingInfo>();
        
        

        public HybridCLRSettingInfo GetSettingInfo(string projectName) 
        {
            foreach (var item in settings)
            {
                if(item.projectName == projectName)
                    return item;
            }
             
            return null;
        }


        public void Add(HybridCLRSettingInfo info) 
        {
            settings.Add(info);
            Save(); 
        }

        public void Remove(HybridCLRSettingInfo info) {
            settings.Remove(info);
            Save();
        }

        public bool IsEmpty() {
            if(settings == null || settings.Count == 0)
                return true;

            return false;
        }

        public void SyncProjects()
        {
            // 同步 创建
            foreach (var project in XFABProjectManager.Instance.Projects)
            {
                HybridCLRSettingInfo info = GetSettingInfo(project.name);
                if (info == null)
                {
                    info = new HybridCLRSettingInfo();
                    info.guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(project)); 
                    Add(info);
                }
            }

            // 同步 移除
            for (int i = 0; i < settings.Count; i++)
            {
                HybridCLRSettingInfo info = settings[i];
                XFABProject project = XFABProjectManager.Instance.GetProject(info.projectName);
                if (project == null)
                {
                    Remove(info);
                    i--;
                }
            } 

        }


        public void SyncHotUpdateSettings() 
        {

            List<AssemblyDefinitionAsset> assemblies = new List<AssemblyDefinitionAsset>();
            List<DefaultAsset> dlls = new List<DefaultAsset>();

            foreach (var item in settings)
            {
                assemblies.AddRange(item.GetAssemblies());
                dlls.AddRange(item.GetDlls());
            }


            // 把当前程序集同步给 Hybrid 
            HybridCLRSettings.Instance.hotUpdateAssemblyDefinitions = assemblies.ToArray();


            List<string> dll_names = new List<string>();
            List<string> dll_dirs = new List<string>();

            foreach (var item in dlls)
            {
                string path = AssetDatabase.GetAssetPath(item);
                if (string.IsNullOrEmpty(path))
                    continue;


                string dllName = Path.GetFileNameWithoutExtension(path);
                string dir = Path.GetDirectoryName(path);

                if (!dll_names.Contains(dllName))
                {
                    dll_names.Add(dllName);
                }

                if (!dll_dirs.Contains(dir))
                {
                    dll_dirs.Add(dir);
                }

            }

            HybridCLRSettings.Instance.hotUpdateAssemblies = dll_names.ToArray();
            HybridCLRSettings.Instance.externalHotUpdateAssembliyDirs = dll_dirs.ToArray();

            HybridCLRSettings.Save();

        }


        public string GetLoadingOrders(string projectName)
        {
            HybridCLRSettingInfo info = GetSettingInfo(projectName);

            if (info == null) return string.Empty;
             
            List<string> orders = new List<string>();

            List<HotUpdateInfo> dlls = new List<HotUpdateInfo>();
            dlls.AddRange(info.infos);

            while (dlls.Count > 0) {
                HotUpdateInfo mini = GetMiniOrder(dlls);
                if (mini == null) continue;
                orders.Add(mini.GetName());
                dlls.Remove(mini);
            }

            return JsonConvert.SerializeObject(orders);
        }


        private HotUpdateInfo GetMiniOrder(List<HotUpdateInfo> dlls ) 
        {
            if (dlls == null || dlls.Count == 0)
                return null;

            HotUpdateInfo mini = dlls[0];

            foreach (HotUpdateInfo info in dlls) 
            {
                if (info.loading_order < mini.loading_order) 
                {
                    mini = info;
                }
            }

            return mini;
        }


        public void Save() 
        {
            string path = string.Format("{0}/../ProjectSettings/XFGameFramework_HybridCLRSettings.asset", Application.dataPath);
            InternalEditorUtility.SaveToSerializedFileAndForget(new UnityEngine.Object[] { _instance }, path, true);
        }

    }
}

